package cn.buk.tms.car.dao;

import cn.buk.tms.dao.AbstractDao;
import cn.buk.tms.entity.misc.*;
import cn.buk.tms.factory.OperationLogFactory;
import cn.buk.tms.car.sc.SearchCarOrderCriteria;
import cn.buk.tms.common.constant.TmsOrderConstant;
import cn.buk.common.util.DateUtil;
import org.springframework.stereotype.Component;

import jakarta.persistence.criteria.*;
import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static cn.buk.tms.common.constant.TmsOrderConstant.*;

@Component
@Transactional
public class CarOrderDaoImpl extends AbstractDao implements CarOrderDao {

  @Override
  public int createCarOrder(RentalCarOrder o) {
    em.persist(o);

    OperationLog log = OperationLogFactory.createCarOrderLog();
    log.setEnterpriseId(o.getEnterpriseId());
    log.setOrderId(o.getId());
    log.setOperator(o.getOperator());
    log.setContent("创建订单");

    em.persist(log);

    return o.getId();
  }

  private List<Predicate> buildPredicates_searchOrders(CriteriaBuilder cb, Root<RentalCarOrder> root, int enterpriseId, SearchCarOrderCriteria sc) {
    List<Predicate> predicates = new ArrayList<>();

    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(RentalCarOrder_.enterpriseId), enterpriseId);
    predicates.add(predicate);

    if (sc.getOrderNo() != null && sc.getOrderNo().trim().length() > 0) {
      //订单号查找
      predicate = cb.equal(root.get(RentalCarOrder_.orderNo), sc.getOrderNo());
      predicates.add(predicate);
    } else {
      //非订单号查找
      if (sc.getStatus() > 0) {
        predicate = cb.equal(root.get(RentalCarOrder_.status), sc.getStatus());
        predicates.add(predicate);
      } else {
        //未提交的订单默认不显示
        predicate = cb.notEqual(root.get(RentalCarOrder_.status), RENTAL_CAR_ORDER_STATUS_NOT_SUBMITTED);
        predicates.add(predicate);

        predicate = cb.notEqual(root.get(RentalCarOrder_.status), RENTAL_CAR_ORDER_STATUS_CANCELED);
        predicates.add(predicate);
      }

      if (sc.getMobile().length() > 0) {
        predicate = cb.like(root.get(RentalCarOrder_.mobile), "%" + sc.getMobile() + "%");
        predicates.add(predicate);
      }

      if (sc.getDdate() != null) {
        //用车日期
        predicate = cb.greaterThan(root.get(RentalCarOrder_.useTime), sc.getDdate());

        final Date endDate = DateUtil.addDays(sc.getDdate(), 1);
        predicate = cb.and(predicate, cb.lessThan(root.get(RentalCarOrder_.useTime), endDate));

        predicates.add(predicate);
      }

      if (sc.getName().length() > 0) {
        //联系人
        predicate = cb.like(root.get(RentalCarOrder_.name), "%" + sc.getName() + "%");
        predicates.add(predicate);
      }
    }

    return predicates;
  }

  @Override
  public List<RentalCarOrder> searchOrders(final int enterpriseId, SearchCarOrderCriteria sc) {
    List<RentalCarOrder> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<RentalCarOrder> cq = cb.createQuery(RentalCarOrder.class);
    Root<RentalCarOrder> root = cq.from(RentalCarOrder.class);
    root.alias("o");

    var predicates0 = buildPredicates_searchOrders(cb, root, enterpriseId, sc);

    //计算根据条件查询得出的数据总数
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<RentalCarOrder> countRoot = countQuery.from(RentalCarOrder.class);
    countRoot.alias("o");

    var predicates1 = buildPredicates_searchOrders(cb, countRoot, enterpriseId, sc);


    // count items
    countQuery.select(cb.count(countRoot)).where(predicates1.toArray(new Predicate[predicates1.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    //查询
    cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

    List<Order> orderByes = new ArrayList<>();
    orderByes.add(cb.desc(root.get("id")));

    cq.orderBy(orderByes);

    int maxResults = count.intValue();
    if (maxResults > 0) {
      sc.getPage().setRowCount(maxResults);

      results = em.createQuery(cq)
              .setFirstResult(sc.getPage().getFirstPosition())
              .setMaxResults(sc.getPage().getPageSize())
              .getResultList();

    }

    if (results == null) {
      results = new ArrayList<>();
    }

    return results;

  }

  @Override
  public RentalCarOrder getOrderById(int orderId) {
    return em.find(RentalCarOrder.class, orderId);
  }

  @Override
  public int cancelRentalCarOrder(int enterpriseId, String username, int orderId) {
    int retCode = em.createQuery("update RentalCarOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate "
            + "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("newStatus", TmsOrderConstant.RENTAL_CAR_ORDER_STATUS_CANCELED)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TmsOrderConstant.RENTAL_CAR_ORDER_STATUS_WAITING)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("取消订单失败");
    }

    OperationLog log = OperationLogFactory.createCarOrderLog();
    log.setEnterpriseId(enterpriseId);
    log.setOrderId(orderId);
    log.setOperator(username);

    String memo = "取消订单";
    log.setContent(memo);

    em.persist(log);

    return retCode;
  }

  @Override
  public List<OperationLog> searchHistory(int enterpriseId, int orderId) {
    return searchOrderHistory(enterpriseId, OPERATING_OBJECT_CAR_ORDER, orderId);
  }

  @Override
  public int updateRentalCarOrderPrice(int enterpriseId, String username, int orderId, double price, double discount, double cost) {
    double profit = price - discount - cost;
    int retCode = em.createQuery("update RentalCarOrder o set " +
            "o.price = :price, o.discount = :discount, o.cost = :cost, o.profit = :profit, o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("price", price)
            .setParameter("discount", discount)
            .setParameter("cost", cost)
            .setParameter("profit", profit)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to update rental order's price.");
    }

    OperationLog log = OperationLogFactory.createCarOrderLog();
    log.setEnterpriseId(enterpriseId);
    log.setOrderId(orderId);
    log.setOperator(username);

    String memo = "修改价格(price: " + price + ", discount: " + discount + ", cost: " + cost;
    log.setContent(memo);

    em.persist(log);

    return retCode;
  }

  @Override
  public int updateRentalCarOrderSupplier(int enterpriseId, String username, int orderId, int supplierId, int paymentMethodId) {
    RentalCarOrder o = em.find(RentalCarOrder.class, orderId);
    if (o.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("RentalCarOrder is not existed.");
    }

    Supplier supplier = em.find(Supplier.class, supplierId);
    if (supplier.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("Supplier is not existed.");
    }

    o.setSupplier(supplier);
    String content = "修改供应商为: " + supplier.getName();

    if (paymentMethodId > 0) {
      PaymentMethod pm = em.find(PaymentMethod.class, paymentMethodId);
      if (pm.getEnterpriseId() != enterpriseId) {
        throw new RuntimeException("PaymentMethod is not existed.");
      }
      o.setPaymentMethod(pm);
      content += ", 支付方式为：" + pm.getName();
    }

    em.merge(o);

    OperationLog log = OperationLogFactory.createCarOrderLog();
    log.setEnterpriseId(o.getEnterpriseId());
    log.setOrderId(o.getId());
    log.setOperator(username);

    log.setContent(content);
    em.persist(log);

    return 1;
  }

  @Override
  public int updateRentalCarOrderPaymentMethod(int enterpriseId, String username, int orderId, int paymentMethodId) {
    RentalCarOrder o = em.find(RentalCarOrder.class, orderId);
    if (o.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("RentalCarOrder is not existed.");
    }

    PaymentMethod pm = em.find(PaymentMethod.class, paymentMethodId);
    if (pm.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("Supplier is not existed.");
    }

    o.setPaymentMethod(pm);
    em.merge(o);

    OperationLog log = OperationLogFactory.createCarOrderLog();
    log.setEnterpriseId(o.getEnterpriseId());
    log.setOrderId(o.getId());
    log.setOperator(username);

    String content = "修改支出方式为: " + pm.getName();
    log.setContent(content);
    em.persist(log);

    return 1;
  }

  @Override
  public int processRentalCarOrder(int enterpriseId, String username, int orderId) {
    int retCode = em.createQuery("update RentalCarOrder o " +
            "set o.status = :newStatus, o.ticketer = :ticketer, o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TmsOrderConstant.RENTAL_CAR_ORDER_STATUS_WAITING)
            .setParameter("newStatus", TmsOrderConstant.RENTAL_CAR_ORDER_STATUS_PROCESSING)
            .setParameter("ticketer", username)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to process rental car order.");
    }

    OperationLog log = OperationLogFactory.createCarOrderLog();
    log.setEnterpriseId(enterpriseId);
    log.setOrderId(orderId);
    log.setOperator(username);

    String content = "处理订单";
    log.setContent(content);
    em.persist(log);

    return retCode;
  }

  @Override
  public int finishRentalCarOrder(int enterpriseId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update RentalCarOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", RENTAL_CAR_ORDER_STATUS_PROCESSING)
            .setParameter("newStatus", RENTAL_CAR_ORDER_STATUS_DONE)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to finish rental car order.");
    }

    OperationLog log = OperationLogFactory.createCarOrderLog();
    log.setEnterpriseId(enterpriseId);
    log.setOrderId(orderId);
    log.setOperator(username);

    String content = "订单处理完毕(" + remark + ")";
    log.setContent(content);
    em.persist(log);

    return retCode;
  }

  @Override
  public List<RentalCarOrder> searchMeetPolicyRentalCarOrders(int enterpriseId) {
    final Date startDate = DateUtil.getYesterday();
    return em.createQuery("select o from RentalCarOrder o "
            + "where o.enterpriseId = :enterpriseId " +
            "and o.status in (:status1, :status2, :status3) " +
            "and o.approvalInfo.violationStatus is null " +
            "and o.createTime >= :createDate "
            + "order by o.id", RentalCarOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("createDate", startDate)
            .setParameter("status1", RENTAL_CAR_ORDER_STATUS_SUBMITTED)
            .setParameter("status2", RENTAL_CAR_ORDER_STATUS_WAITING)
            .setParameter("status3", RENTAL_CAR_ORDER_STATUS_PROCESSING)
            .getResultList();
  }

  @Override
  public int updateRentalCarOrderViolationStatus(int enterpriseId, String username, int orderId, int status, String remark) {
    int retCode = em.createQuery("update RentalCarOrder o set " +
            "o.approvalInfo.violationStatus = :status, " +
            "o.approvalInfo.violationReason = :reason, " +
            "o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", status)
            .setParameter("reason", remark)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("updateRentalCarOrderViolationStatus failed.");
    }
    OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_CAR_ORDER);
    log.setOperator(username);
    log.setOrderId(orderId);
    String content = status == 0 ? "未违反差旅政策" : "违反了差旅政策";
    content += "(" + remark + ")";
    log.setContent(content);
    em.persist(log);
    return retCode;
  }

  @Override
  public int updateRentalOrderApprover(int enterpriseId, String username, int orderId, String approver) {
    int retCode = em.createQuery("update RentalCarOrder o set o.approvalInfo.specifiedApprover = :approver," +
            "o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("approver", approver)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode > 0) {
      OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_CAR_ORDER);
      log.setOperator(username);
      log.setOrderId(orderId);
      log.setContent("分派审批人为 " + approver);
      em.persist(log);
    }
    return retCode;
  }

  @Override
  public List<RentalCarOrder> searchNeedAssignApproverRentalOrders(int enterpriseId) {
    final Date startDate = DateUtil.getYesterday();
    return em.createQuery("select o from RentalCarOrder o "
            + "where o.enterpriseId = :enterpriseId " +
            "and o.status = :status " +
            "and o.approvalInfo.specifiedApprover is null " +
            "and o.createTime >= :createDate "
            + "order by o.id", RentalCarOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", TmsOrderConstant.RENTAL_CAR_ORDER_STATUS_SUBMITTED)
            .setParameter("createDate", startDate)
            .getResultList();
  }

  @Override
  public int rollbackOrder(int enterpriseId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update RentalCarOrder o " +
            "set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
            "where o.id = :id " +
            "and o.enterpriseId = :enterpriseId and o.status in (:oldStatus1)")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus1", RENTAL_CAR_ORDER_STATUS_DONE)
            .setParameter("newStatus", RENTAL_CAR_ORDER_STATUS_PROCESSING)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("To rollback rental car order failed.");
    }

    OperationLog log = OperationLogFactory.createOperationLog(enterpriseId, OPERATING_OBJECT_CAR_ORDER);
    log.setOperator(username);
    log.setOrderId(orderId);
    log.setContent("租车单回滚: " + remark);
    em.persist(log);

    return retCode;
  }

  /**
   *
   * @param enterpriseId
   * @param username
   * @param orderId
   * @param contentType 修改的类型
   *                    1-联系人
   *                    2-联系电话
   *                    3-备注
   * @param content
   * @return
   */
  @Override
  public int updateRentalCarOrder(int enterpriseId, String username, int orderId, int contentType, String content) {
    String remark = null;
    String hql = "update RentalCarOrder o set ";
    if (contentType == 1) {
      hql += "o.name = :content, ";
      remark = "修改联系人";
    } else if (contentType == 2) {
      hql += "o.mobile = :content, ";
      remark = "修改联系电话";
    } else {
      hql += "o.remark = :content, ";
      remark = "修改备注";
    }
    hql += "o.lastUpdate = :lastUpdate " +
            "where o.id = :id " +
            "and o.enterpriseId = :enterpriseId and o.status <> :oldStatus";
    int retCode = em.createQuery(hql)
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", RENTAL_CAR_ORDER_STATUS_DONE)
            .setParameter("content", content)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to updateRentalCarOrder.");
    }

    OperationLog log = OperationLogFactory.createOperationLog(enterpriseId, OPERATING_OBJECT_CAR_ORDER);
    log.setOperator(username);
    log.setOrderId(orderId);
    log.setContent(remark + "为:" + content);
    em.persist(log);

    return retCode;
  }

}
